package com.tianji.learning.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.client.user.UserClient;
import com.tianji.api.dto.user.UserDTO;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constant.LearningConstant;
import com.tianji.learning.constant.RedisConstant;
import com.tianji.learning.domain.po.PointsBoard;
import com.tianji.learning.domain.po.PointsBoardSeason;
import com.tianji.learning.domain.query.PointsBoardQuery;
import com.tianji.learning.domain.vo.PointsBoardItemVO;
import com.tianji.learning.domain.vo.PointsBoardVO;
import com.tianji.learning.mapper.PointsBoardMapper;
import com.tianji.learning.service.IPointsBoardSeasonService;
import com.tianji.learning.service.IPointsBoardService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.learning.utils.TableInfoContext;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import javax.validation.constraints.Min;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 学霸天梯榜 服务实现类
 * </p>
 *
 * @author hrz
 * @since 2025-08-31
 */
@Service
@RequiredArgsConstructor
@Slf4j
@SuppressWarnings("all")
public class PointsBoardServiceImpl extends ServiceImpl<PointsBoardMapper, PointsBoard> implements IPointsBoardService {

    private final RedisTemplate redisTemplate;
    private final IPointsBoardSeasonService pointsBoardSeasonService;
    private final UserClient userClient;
    private final PointsBoardMapper pointsBoardMapper;

    @Override
    public PointsBoardVO getPointsBoard(PointsBoardQuery query) {
        Long season = query.getSeason(); //赛季id
        List<PointsBoardItemVO> boardList = new ArrayList<>(); //最终的结果
        //判断查询的赛季时间
        PointsBoardSeason one = pointsBoardSeasonService.lambdaQuery()
                .eq(PointsBoardSeason::getId, season)
                .one();
        if (one == null) {
            throw new IllegalArgumentException("赛季不存在");
        }
        //组合key
        String key = RedisConstant.POINTS_BOARD_KEY_PREFIX +
                one.getBeginTime().format(DateTimeFormatter.ofPattern("yyyyMM"));
        //判断是否是当前赛季
        PointsBoard mine = null;
        List<PointsBoard> list = new ArrayList<>();
        if (season == null || season <= 0) {
            //查询当前赛季我的排行和积分
            mine = getMyCurrentSeasonPointsAndRank(key);
            //查询当前赛季别人的积分和排名以及姓名
            list = getCurrentSeasonPointsAndRank
                    (key,query.getPageNo(),query.getPageSize());
            //查询姓名
            Set<Long> collect = list.stream()
                    .map(s -> s.getUserId())
                    .collect(Collectors.toSet());
            List<UserDTO> userDTOS = userClient.queryUserByIds(collect);
            Map<Long, String> NameMaps = userDTOS.stream()
                    .collect(Collectors.toMap(UserDTO::getId, UserDTO::getName));
            for (PointsBoard pointsBoard : list) {
                PointsBoardItemVO itemVO = new PointsBoardItemVO();
                itemVO.setRank(pointsBoard.getRank());
                itemVO.setPoints(pointsBoard.getPoints());
                itemVO.setName(NameMaps.get(pointsBoard.getUserId()));
                boardList.add(itemVO);
            }
        } else {
            //查询历史赛季我的排行和积分
            mine = getMyHistorySeasonPointsAndRank(season);
            //查询历史赛季别人的积分和排名以及姓名
            list = getHistorySeasonPointsAndRank(season,query.getPageNo(),query.getPageSize());
            //查询姓名
            Set<Long> collect = list.stream()
                    .map(s -> s.getUserId())
                    .collect(Collectors.toSet());
            List<UserDTO> userDTOS = userClient.queryUserByIds(collect);
            Map<Long, String> NameMaps = userDTOS.stream()
                    .collect(Collectors.toMap(UserDTO::getId, UserDTO::getName));
            for (PointsBoard pointsBoard : list) {
                PointsBoardItemVO itemVO = new PointsBoardItemVO();
                itemVO.setRank(pointsBoard.getRank());
                itemVO.setPoints(pointsBoard.getPoints());
                itemVO.setName(NameMaps.get(pointsBoard.getUserId()));
                boardList.add(itemVO);
            }
        }
        //封装
        PointsBoardVO vo = new PointsBoardVO();
        vo.setRank(mine.getRank());
        vo.setPoints(mine.getPoints());
        vo.setBoardList(boardList);
        return vo;  
    }

    @Override
    public void createNewTable(Integer seasonId) {
        //根据赛季id创建一张新表
        log.info("PointsBoardServiceImpl创建了一张新表");
        //1.组合表名
        String tableName = LearningConstant.POINT_BOARD_TABLE_PREFIX + seasonId;
        //2.调用sql创建
        pointsBoardMapper.createNewTable(tableName);
    }

    private List<PointsBoard> getHistorySeasonPointsAndRank(Long season,
        @Min(value = 1, message = "页码不能小于1") Integer pageNo,
        @Min(value = 1, message = "每页查询数量不能小于1") Integer pageSize) {
        //1.计算数据库名称
        String name = LearningConstant.POINT_BOARD_TABLE_PREFIX + season;
        TableInfoContext.setInfo(name);
        //2.去相应数据库中查询数据
        List<PointsBoard> list = this.lambdaQuery()
                .orderByDesc(PointsBoard::getPoints)
                .page(new Page<>(pageNo, pageSize))
                .getRecords();
        if(list == null || list.size() == 0) {
            return new ArrayList<>();
        }
        //3.清空ThreadLocal中的数据
        TableInfoContext.remove();
        return list;
    }


    private PointsBoard getMyHistorySeasonPointsAndRank(Long season) {
        //1.计算数据库名称
        String name = LearningConstant.POINT_BOARD_TABLE_PREFIX + season;
        TableInfoContext.setInfo(name);
        //2.去相应数据库中查询自己的数据
        PointsBoard one = this.lambdaQuery()
                .eq(PointsBoard::getUserId, UserContext.getUser())
                .one();
        if(one == null){
            //说明没有数据
            //默认0分,0排名
            one = new PointsBoard();
            one.setPoints(0);
            one.setRank(0);
        }
        //3.清空ThreadLocal中的数据
        TableInfoContext.remove();
        return one;
    }

    @Override
    public List<PointsBoard> getCurrentSeasonPointsAndRank(String key,Integer pageNo,Integer pageSize) {
        //去查询其他人的排名和积分
        List<PointsBoard> list = new ArrayList<>();
        //计算下标
        int start = (pageNo - 1) * pageSize;
        int end = start + pageSize - 1;
        Set<ZSetOperations.TypedTuple> set = redisTemplate.
                opsForZSet().reverseRangeWithScores(key, start, end);
        int rank = (pageNo-1)*pageSize+1; //用来计算实际排名
        for (ZSetOperations.TypedTuple typedTuple : set) {
            Long userid =(Long) typedTuple.getValue();//用户id
            int score = typedTuple.getScore().intValue();//积分
            PointsBoard pointsBoard = new PointsBoard();
            pointsBoard.setId(Long.valueOf(rank)); //行的id和排名一致
            pointsBoard.setUserId(userid);
            pointsBoard.setRank(rank);
            pointsBoard.setPoints(score);
            list.add(pointsBoard);
            rank++;
        }
        return list;
    }

    private PointsBoard getMyCurrentSeasonPointsAndRank(String key) {
        //查询当前赛季我的积分和排名
        Double score = redisTemplate.opsForZSet().score(key, UserContext.getUser());
        if(score == null){
            //默认0分
            score = 0D;
        }
        Long rank = redisTemplate.opsForZSet().reverseRank(key, UserContext.getUser());
        if(rank == null){
            //默认0名
            rank = 0L;
        }
        //封装
        PointsBoard pointsBoard = new PointsBoard();
        pointsBoard.setPoints(score.intValue());
        pointsBoard.setRank(rank.intValue());
        return pointsBoard;
    }
}
